#include "StdAfx.h"
#include "ExplorerController.h"
#include "..\FilerFrogUtils\FilerFrogUtils.h"
#include <map>

#pragma warning(disable:4482)

#define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0]) 
#define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1]) 

HWND ExplorerController::s_ListHwnd = NULL;

ExplorerController::ExplorerController():
m_ExplorerHwnd(NULL),
m_pShellBrowser(NULL),
m_pShellView(NULL),
m_pFolderView(NULL)
{
	CoInitialize(NULL);
}

ExplorerController::~ExplorerController()
{
	CoInitializeEx(NULL, COINIT_MULTITHREADED);
}

void ExplorerController::ReleaseComPointers()
{
	m_pShellBrowser = NULL;
	m_pShellView = NULL;
	m_pFolderView = NULL;
}

void ExplorerController::SetSitePtr(CComPtr<IUnknown> spUnkSite)
{
	ReleaseComPointers();
	
	if (spUnkSite != NULL)
	{
		CComPtr<IServiceProvider> pServiceProvider = NULL;

		if (SUCCEEDED(spUnkSite->QueryInterface(IID_IServiceProvider , (LPVOID*)&pServiceProvider)))
		{			
			if (SUCCEEDED(pServiceProvider->QueryService(SID_SShellBrowser, IID_IShellBrowser, (LPVOID*)&m_pShellBrowser)))
			{
				if (SUCCEEDED(m_pShellBrowser->QueryActiveShellView(&m_pShellView)))
				{
					if (SUCCEEDED(m_pShellView->QueryInterface(IID_IFolderView, (LPVOID*)&m_pFolderView)))
					{
						// All Ok
					}
				}
			}
		}
	}
}

bool ExplorerController::RefreshFolderView()
{
	if (m_pShellView != NULL)
	{
		m_pShellView->Refresh();

		MSG msg;

		Sleep(10);
		
		while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE ))
		{ 
			if (GetMessage(&msg, NULL, 0, 0)) 
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
				Sleep(1);
			}
			else
			{
				break;
			}
		}

		return true;
	}

	return false;
}

bool ExplorerController::InvertFilesSelection()
{
	bool retVal = false;

	if (m_pFolderView != NULL && m_pShellView != NULL)
	{
		int itemsCount = 0;
		int selectedItemsCount = 0;

		if (SUCCEEDED(m_pFolderView->ItemCount(SVGIO_ALLVIEW, &itemsCount)) &&
			SUCCEEDED(m_pFolderView->ItemCount(SVGIO_SELECTION, &selectedItemsCount)))
		{
			IDataObject* pIDataObject = NULL;

			if (SUCCEEDED(m_pShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject,(LPVOID*)&pIDataObject)))
			{
				// Selects All Items
				for (int i=0; i<itemsCount; i++)
				{
					m_pFolderView->SelectItem(i, SVSI_SELECT);
				}

				if (pIDataObject)
				{
					FORMATETC fmte;
					STGMEDIUM stgmedium;

					ZeroMemory((LPVOID)&stgmedium, sizeof(STGMEDIUM));
					ZeroMemory((LPVOID)&fmte, sizeof(FORMATETC));

					fmte.tymed = TYMED_HGLOBAL;            
					fmte.lindex = -1;            
					fmte.dwAspect = DVASPECT_CONTENT;
					fmte.cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);

					if (SUCCEEDED(pIDataObject->GetData(&fmte,&stgmedium)))
					{
						LPMALLOC pMalloc = NULL;

						//Get pointer to shell alloc
						if (SUCCEEDED(SHGetMalloc(&pMalloc)))							
						{
							LPIDA pida = (LPIDA)GlobalLock(stgmedium.hGlobal);

							if (pida)
							{
								// Deselect All Items
								for (UINT i=0; i<pida->cidl; i++)
								{
									LPCITEMIDLIST pidl = GetPIDLItem(pida,i);
									m_pShellView->SelectItem(pidl, SVSI_DESELECT);					
								}
								//Move focus to the folder view so that
								//the selected items show properly
								m_pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);

								retVal = true;
							}
						}

						//Clean up
						GlobalUnlock(stgmedium.hGlobal);
						ReleaseStgMedium(&stgmedium);

						if (pMalloc)
						{			
							pMalloc->Release();
						}
					}

					pIDataObject->Release();
				}
			}
		}
	}

	return retVal;
}

bool ExplorerController::GetSelectedItemsPath(list<wstring>& itemsList)
{
	bool retVal = false;
	itemsList.clear();

	if (m_pShellView != NULL)
	{
		IDataObject* pIDataObject = NULL;

		if (SUCCEEDED(m_pShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject,(LPVOID*)&pIDataObject)))
		{
			if (pIDataObject)
			{
				FORMATETC fmte;
				STGMEDIUM stgmedium;

				ZeroMemory((LPVOID)&stgmedium, sizeof(STGMEDIUM));
				ZeroMemory((LPVOID)&fmte, sizeof(FORMATETC));

				fmte.tymed = TYMED_HGLOBAL;            
				fmte.lindex = -1;            
				fmte.dwAspect = DVASPECT_CONTENT;
				fmte.cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);

				if (SUCCEEDED(pIDataObject->GetData(&fmte,&stgmedium)))
				{
					LPMALLOC pMalloc = NULL;

					//Get pointer to shell alloc
					if (SUCCEEDED(SHGetMalloc(&pMalloc)))							
					{
						LPIDA pida = (LPIDA)GlobalLock(stgmedium.hGlobal);

						if (pida)
						{
							retVal = true;

							LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);

							for (UINT i=0; i<pida->cidl; i++)
							{
								LPCITEMIDLIST pidl = GetPIDLItem(pida,i);
								LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidl);

								wchar_t buffer[1024];

								if (SHGetPathFromIDList(pidlFull, buffer) == TRUE)
								{
									itemsList.push_back(wstring(buffer));
								}

								pMalloc->Free(pidlFull);
								pidlFull=NULL;
							}
						}
					}

					//Clean up
					GlobalUnlock(stgmedium.hGlobal);
					ReleaseStgMedium(&stgmedium);

					if (pMalloc)
					{			
						pMalloc->Release();
					}
				}

				pIDataObject->Release();
			}
		}
	}

	return retVal;
}


bool ExplorerController::SelectItemsByPathList(const list<wstring>& itemsList)
{
	bool retVal = false;

	map<wstring, int> filesMap;

	for (list<wstring>::const_iterator iter = itemsList.begin(); iter != itemsList.end(); iter++)
	{
		filesMap[*iter] = 1;
	}

	if (m_pShellView != NULL && m_pFolderView != NULL)
	{
		int itemsCount = 0;
		int selectedItemsCount = 0;

		RefreshFolderView();

		if (SUCCEEDED(m_pFolderView->ItemCount(SVGIO_ALLVIEW, &itemsCount)) &&
			SUCCEEDED(m_pFolderView->ItemCount(SVGIO_SELECTION, &selectedItemsCount)))
		{
			IDataObject* pIDataObject = NULL;			

			if (SUCCEEDED(m_pShellView->GetItemObject(SVGIO_ALLVIEW, IID_IDataObject,(LPVOID*)&pIDataObject)))
			{
				// Deselects All Items
				for (int i=0; i<itemsCount; i++)
				{
					m_pFolderView->SelectItem(i, SVSI_DESELECT);
				}

				if (pIDataObject)
				{
					FORMATETC fmte;
					STGMEDIUM stgmedium;

					ZeroMemory((LPVOID)&stgmedium, sizeof(STGMEDIUM));
					ZeroMemory((LPVOID)&fmte, sizeof(FORMATETC));

					fmte.tymed = TYMED_HGLOBAL;            
					fmte.lindex = -1;            
					fmte.dwAspect = DVASPECT_CONTENT;
					fmte.cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);

					if (SUCCEEDED(pIDataObject->GetData(&fmte,&stgmedium)))
					{
						LPMALLOC pMalloc = NULL;

						//Get pointer to shell alloc
						if (SUCCEEDED(SHGetMalloc(&pMalloc)))							
						{
							LPIDA pida = (LPIDA)GlobalLock(stgmedium.hGlobal);

							if (pida)
							{									
								retVal = true;

								LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);

								for (UINT i=0; i<pida->cidl; i++)
								{
									LPCITEMIDLIST pidl = GetPIDLItem(pida,i);
									LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidl);

									wchar_t buffer[1024];

									if (SHGetPathFromIDList(pidlFull, buffer) == TRUE)
									{
										wstring filePath = buffer;

										if (filesMap[filePath] == 1)
										{
											m_pShellView->SelectItem(pidl, SVSI_SELECT);
										}
									}

									pMalloc->Free(pidlFull);
									pidlFull=NULL;
								}

								//Move focus to the folder view so that
								//the selected items show properly
								m_pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);
							}
						}

						//Clean up
						GlobalUnlock(stgmedium.hGlobal);
						ReleaseStgMedium(&stgmedium);

						pMalloc->Release();
					}

					pIDataObject->Release();
				}
			}
		}
	}

	return retVal;
}

bool ExplorerController::SortItemsByPathListWithLastConfig(bool silentMode)
{
	DesktopSortConfig lastDesktopSortConfig;
	lastDesktopSortConfig.LoadFromRegistry();
	return SortItemsByPathList(lastDesktopSortConfig, false, silentMode);
}

bool ExplorerController::SortItemsByPathList(DesktopSortConfig& desktopSortConfig, bool clearLastDesktop, bool silentMode)
{
	if (clearLastDesktop)
	{
		desktopSortConfig.originalWallpaperPath[0] = 0;
	}	

	bool retVal = false;

	if (m_pShellView != NULL && m_pFolderView != NULL)
	{
		int itemsCount = 0;
		int selectedItemsCount = 0;

		if (SUCCEEDED(m_pFolderView->ItemCount(SVGIO_ALLVIEW, &itemsCount)) &&
			SUCCEEDED(m_pFolderView->ItemCount(SVGIO_SELECTION, &selectedItemsCount)))
		{
			IDataObject* pIDataObject = NULL;			

			if (SUCCEEDED(m_pShellView->GetItemObject(SVGIO_ALLVIEW, IID_IDataObject,(LPVOID*)&pIDataObject)))
			{
				// Deselects All Items
				for (int i=0; i<itemsCount; i++)
				{
					m_pFolderView->SelectItem(i, SVSI_DESELECT);					
				}

				if (pIDataObject)
				{
					FORMATETC fmte;
					STGMEDIUM stgmedium;

					ZeroMemory((LPVOID)&stgmedium, sizeof(STGMEDIUM));
					ZeroMemory((LPVOID)&fmte, sizeof(FORMATETC));

					fmte.tymed = TYMED_HGLOBAL;            
					fmte.lindex = -1;            
					fmte.dwAspect = DVASPECT_CONTENT;
					fmte.cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);

					if (SUCCEEDED(pIDataObject->GetData(&fmte,&stgmedium)))
					{
						LPMALLOC pMalloc = NULL;

						//Get pointer to shell alloc
						if (SUCCEEDED(SHGetMalloc(&pMalloc)))							
						{
							LPIDA pida = (LPIDA)GlobalLock(stgmedium.hGlobal);

							if (pida)
							{									
								retVal = true;

								LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);

								DesktopSorter desktopSorter(m_pFolderView);

								wstring desktopFolder = FilerFrogUtils::ToLower(FilerFrogUtils::GetDesktopDirectory());
								wstring desktopFolderAll = FilerFrogUtils::ToLower(FilerFrogUtils::GetDesktopAllDirectory());

								for (UINT i=0; i<pida->cidl; i++)
								{
									LPCITEMIDLIST pidl = GetPIDLItem(pida,i);
									LPITEMIDLIST pidlFull = ILCombine(pidlFolder, pidl);

									wchar_t buffer[1024];

									if (SHGetPathFromIDList(pidlFull, buffer) == TRUE)
									{
										wstring filePath = buffer;

										DesktopItemType desktopItemType = DesktopItemType::File;

										if (FilerFrogUtils::IsDirectory(filePath))
										{
											wstring filePathLower = FilerFrogUtils::ToLower(filePath).substr(0, desktopFolder.length());
											wstring filePathAllLower = FilerFrogUtils::ToLower(filePath).substr(0, desktopFolderAll.length());

											if (filePathLower == desktopFolder || filePathAllLower == desktopFolderAll)
											{
												desktopItemType = DesktopItemType::Folder;
											}
											else
											{
												desktopItemType = DesktopItemType::SystemFolder;
											}											
										}

										desktopSorter.AddDesktopItem(pidl, desktopItemType, filePath);
									}
									else
									{
										CComPtr<IShellFolder> desktopFolder;
										SHGetDesktopFolder(&desktopFolder);
										STRRET strret;
										desktopFolder->GetDisplayNameOf(pidl, SHGDN_NORMAL, &strret);
										wstring name = strret.pOleStr;
										desktopSorter.AddDesktopItem(pidl, DesktopItemType::System, name);
									}

									pMalloc->Free(pidlFull);
									pidlFull = NULL;
								}
							
								desktopSorter.SetSilentMode(silentMode);
								desktopSorter.ArrangeDesktop(desktopSortConfig);

								//Move focus to the folder view so that
								//the selected items show properly
								m_pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);
							}	
						}

						//Clean up
						GlobalUnlock(stgmedium.hGlobal);
						ReleaseStgMedium(&stgmedium);

						if (pMalloc)
						{		
							pMalloc->Release();
						}
					}

					pIDataObject->Release();
				}
			}
		}
	}

	return retVal;
}
